package com.zl.gmadmin.configuration.secutity;

import com.zl.gmadmin.configuration.secutity.filter.JwtAuthenticationFilter;
import com.zl.gmadmin.configuration.secutity.ignore.CustomConfig;
import com.zl.gmadmin.service.auth.CustomUserDetailsService;
import com.zl.gmadmin.utils.LogUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;

/**
 * @author zhangliang
 * @version 1.0
 * @date 2021/2/2 10:40
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    private final CustomConfig customConfig;

    private final AccessDeniedHandler accessDeniedHandler;

    private final  UserDetailsService customUserDetailsService;


    public WebSecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter, CustomConfig customConfig, AccessDeniedHandler accessDeniedHandler, UserDetailsService customUserDetailsService){
        this.jwtAuthenticationFilter=jwtAuthenticationFilter;
        this.customConfig=customConfig;
        this.accessDeniedHandler=accessDeniedHandler;
        this.customUserDetailsService = customUserDetailsService;
    }

    /**
     * 初始化  AuthenticationManager管理器
      */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 这里对登录的用户信息验证，需要传入用户服务信息
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(customUserDetailsService).passwordEncoder(encoder());
    }


    /**
     * 配置密码加密策略
     */
    @Bean
    public BCryptPasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors()
//               关闭csrf
                .and().csrf().disable()
                // 登录行为由自己实现，参考 AuthController#login
                .formLogin().disable()
                .httpBasic().disable()
                //认证请求
                .authorizeRequests()
                //所有的请求都需要认证执行两次会报错，源码里面加了判断，开启动态验证url，就不要开启这个
//               .anyRequest()
//               .authenticated()
                //rbac动态url认证，判断数据库是否存在此接口地址，没有返回权限不足
                .anyRequest()
                .access("@RBACAuthorityService.hasPermission(request,authentication)")
                //登出行为自定义
                .and().logout().disable()
                //Session管理
                .sessionManagement()
                // 因为使用了JWT，所以这里不管理Session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                //异常管理
                .and().exceptionHandling().accessDeniedHandler(accessDeniedHandler);

        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {

        if (customConfig.getIgnoreConfig()==null) {
            LogUtil.debug("我是空的");
            return;
        }

        WebSecurity and = web.ignoring().and();

        customConfig.getIgnoreConfig().getGet().forEach(url ->
            and.ignoring().antMatchers(HttpMethod.GET, url));

        // 忽略 POST
        customConfig.getIgnoreConfig().getPost().forEach(url -> and.ignoring().antMatchers(HttpMethod.POST, url));

        // 忽略 DELETE
        customConfig.getIgnoreConfig().getDelete().forEach(url -> and.ignoring().antMatchers(HttpMethod.DELETE, url));

        // 忽略 PUT
        customConfig.getIgnoreConfig().getPut().forEach(url -> and.ignoring().antMatchers(HttpMethod.PUT, url));

        // 忽略 HEAD
        customConfig.getIgnoreConfig().getHead().forEach(url -> and.ignoring().antMatchers(HttpMethod.HEAD, url));

        // 忽略 PATCH
        customConfig.getIgnoreConfig().getPatch().forEach(url -> and.ignoring().antMatchers(HttpMethod.PATCH, url));

        // 忽略 OPTIONS
        customConfig.getIgnoreConfig().getOptions().forEach(url -> and.ignoring().antMatchers(HttpMethod.OPTIONS, url));

        // 忽略 TRACE
        customConfig.getIgnoreConfig().getTrace().forEach(url -> and.ignoring().antMatchers(HttpMethod.TRACE, url));

        // 按照请求格式忽略
        customConfig.getIgnoreConfig().getPattern().forEach(url -> and.ignoring().antMatchers(url));
    }
}
